package IntelIpps

/*
#cgo CFLAGS: -ID:/DevelopTools/IPPS2022/ipp/2021.12/include
#cgo LDFLAGS: -LD:/DevelopTools/IPPS2022/ipp/2021.12/lib -lipps -lippcore
#include <ipp.h>
#include <stdlib.h>
*/
import "C"
import (
	"errors"
	"unsafe"
)

// FIR 状态
type firState struct {
	spec      *C.IppsFIRSpec_32f
	buffer    []byte
	dlyLine   []float32
	numCoeffs int
}

var firStates = make(map[int]*firState) // key: numCoeffs (for simplicity)

func (i *IppImpl) FIRInitFloat(coeffs []float32, algType AlgType) error {
	if len(coeffs) == 0 {
		return errors.New("FIR coeffs cannot be empty")
	}
	numCoeffs := len(coeffs)

	// 释放旧状态
	if old, ok := firStates[numCoeffs]; ok {
		i.FIRFreeFloat()
		if old != nil {
			return errors.New("FIR already initialized with same coeff length")
		}
	}

	var specSize, bufSize C.int
	status := C.ippsFIRSRGetSize(C.int(numCoeffs), C.ipp32f, &specSize, &bufSize)
	if status != C.ippStsNoErr {
		return errors.New("ippsFIRSRGetSize failed")
	}

	spec := (*C.IppsFIRSpec_32f)(C.malloc(C.size_t(specSize)))
	if spec == nil {
		return errors.New("malloc failed for FIR spec")
	}

	buffer := make([]byte, bufSize)
	dlyLine := make([]float32, numCoeffs-1)
	for idx := range dlyLine {
		dlyLine[idx] = 0.0
	}

	ippAlg := C.IppAlgType(algType)
	status = C.ippsFIRSRInit_32f(
		(*C.Ipp32f)(unsafe.Pointer(&coeffs[0])),
		C.int(numCoeffs),
		ippAlg,
		spec,
	)
	if status != C.ippStsNoErr {
		C.free(unsafe.Pointer(spec))
		return errors.New("ippsFIRSRInit_32f failed")
	}

	firStates[numCoeffs] = &firState{
		spec:      spec,
		buffer:    buffer,
		dlyLine:   dlyLine,
		numCoeffs: numCoeffs,
	}

	return nil
}

func (i *IppImpl) FIRFloat(src []float32) []float32 {
	if len(src) == 0 {
		return nil
	}
	numCoeffs := 0
	state := (*firState)(nil)
	for k, s := range firStates {
		numCoeffs = k
		state = s
		break
	}
	if state == nil {
		return src // 未初始化，直接返回
	}
	println(numCoeffs)
	dst := make([]float32, len(src))
	status := C.ippsFIRSR_32f(
		(*C.Ipp32f)(unsafe.Pointer(&src[0])),
		(*C.Ipp32f)(unsafe.Pointer(&dst[0])),
		C.int(len(src)),
		state.spec,
		(*C.Ipp32f)(unsafe.Pointer(&state.dlyLine[0])),
		(*C.Ipp32f)(unsafe.Pointer(&state.dlyLine[0])),
		(*C.Ipp8u)(unsafe.Pointer(&state.buffer[0])),
	)
	if status != C.ippStsNoErr {
		return src
	}
	return dst
}

func (i *IppImpl) FIRFreeFloat() {
	for _, s := range firStates {
		if s.spec != nil {
			C.free(unsafe.Pointer(s.spec))
		}
	}
	firStates = make(map[int]*firState)
}
